#pragma once

// ==================包含头文件====================
#include "gpio.hpp"
#include "hal_delay.hpp"


#include <project_config.h>
#include <stm32f4xx_hal.h>
// ==================包含头文件====================

namespace hal::detail_spi
{

    template<uint32_t SPI_Base>
    class spi
    {
        // HAL句柄导出
        static inline SPI_HandleTypeDef hspi;       ///< SPI2主句柄
        static inline DMA_HandleTypeDef hdma_spi_tx;///< SPI2发送DMA句柄
        static inline DMA_HandleTypeDef hdma_spi_rx;///< SPI2接收DMA句柄
        static void dma_init()
        {
            __HAL_RCC_DMA1_CLK_ENABLE();

            HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
            HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);

            HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
            HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);

            // DMA发送配置
            hdma_spi_tx.Instance = DMA1_Stream4;
            hdma_spi_tx.Init = {
                    .Channel = DMA_CHANNEL_0,
                    .Direction = DMA_MEMORY_TO_PERIPH,
                    .PeriphInc = DMA_PINC_DISABLE,
                    .MemInc = DMA_MINC_ENABLE,
                    .PeriphDataAlignment = DMA_PDATAALIGN_BYTE,// 接收半字
                    .MemDataAlignment = DMA_MDATAALIGN_BYTE,   // 跟字节序有关
                    .Mode = DMA_NORMAL,
                    .Priority = DMA_PRIORITY_HIGH,
                    .FIFOMode = DMA_FIFOMODE_DISABLE};
            HAL_DMA_Init(&hdma_spi_tx);

            // DMA接收配置
            hdma_spi_rx.Instance = DMA1_Stream3;
            hdma_spi_rx.Init = {
                    .Channel = DMA_CHANNEL_0,
                    .Direction = DMA_PERIPH_TO_MEMORY,
                    .PeriphInc = DMA_PINC_DISABLE,
                    .MemInc = DMA_MINC_ENABLE,
                    .PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD,// 接收半字
                    .MemDataAlignment = DMA_MDATAALIGN_HALFWORD,
                    .Mode = DMA_NORMAL,
                    .Priority = DMA_PRIORITY_MEDIUM,
                    .FIFOMode = DMA_FIFOMODE_DISABLE};
            HAL_DMA_Init(&hdma_spi_rx);

            __HAL_LINKDMA(&hspi, hdmatx, hdma_spi_tx);
            __HAL_LINKDMA(&hspi, hdmarx, hdma_spi_rx);

            // 禁用其他不必要的中断（提升性能）
            __HAL_SPI_DISABLE_IT(&hspi, SPI_IT_TXE);
            // __HAL_DMA_DISABLE_IT(&hdma_spi_tx, DMA_IT_HT);// 禁用半传输中断
            // __HAL_DMA_DISABLE_IT(&hdma_spi_tx, DMA_IT_FE);


            // __HAL_DMA_ENABLE_IT(&hdma_spi_rx, DMA_IT_TC);
            hdma_spi_tx.Instance->FCR &= ~ DMA_IT_FE;// 禁用FIFO错误中断
            hdma_spi_rx.Instance->CR &= ~ (DMA_IT_TC| DMA_IT_HT);// 使能传输完成中断 禁用半传输中断

            SET_BIT(hspi.Instance->CR2, SPI_CR2_TXDMAEN);// 使能DMA传输
        }

        // using cs_pin = gpio::portB::pin12;
        // using miso_pin = gpio::portC::pin2;
        // using mosi_pin = gpio::portC::pin3;
        // using sck_pin = gpio::portB::pin13;

        static SPI_TypeDef * getInstance(){return reinterpret_cast<SPI_TypeDef *>(SPI_Base);}

        constexpr static void enable_clock()
        {
            switch (SPI_Base) {
                case SPI1_BASE:
                    __HAL_RCC_SPI1_CLK_ENABLE();
                    break;
                    case SPI2_BASE:
                    __HAL_RCC_SPI2_CLK_ENABLE();
                    break;
                case SPI3_BASE:
                    __HAL_RCC_SPI3_CLK_ENABLE();
                    break;
                    default:break;
            }
        }

        // 复用功能映射函数（编译期）
        static constexpr uint32_t getAlternate()
        {
            switch (SPI_Base) {
                case SPI1_BASE: return GPIO_AF5_SPI1;
                case SPI2_BASE: return GPIO_AF5_SPI2;
                case SPI3_BASE: return GPIO_AF6_SPI3;
                default:        return 0;
            }
        }

        struct pinConfig
        {
            uint32_t port;
            uint32_t pin;
        };

        struct SPI_Pin_Config
        {
            pinConfig cs;
            pinConfig sck;
            pinConfig miso;
            pinConfig mosi;
        };

        // SPI实例对应的编译期引脚配置
        static constexpr SPI_Pin_Config getPinConfig()
        {
            switch (SPI_Base) {
                case SPI1_BASE:
                    /**SPI2 GPIO Configuration
                    PC2     ------> SPI2_MISO
                    PC3     ------> SPI2_MOSI
                    PB13     ------> SPI2_SCK
                    PB12     ------> SPI2_NSS
                    */
                    return {
                        .cs   = {GPIOB_BASE, GPIO_PIN_12},
                        .sck  = {GPIOB_BASE, GPIO_PIN_13},
                        .miso = {GPIOC_BASE, GPIO_PIN_2},
                        .mosi = {GPIOC_BASE, GPIO_PIN_3}
                    };
                case SPI2_BASE:
                    return {
                        .cs   = {GPIOB_BASE, GPIO_PIN_12},
                        .sck  = {GPIOB_BASE, GPIO_PIN_13},
                        .miso = {GPIOC_BASE, GPIO_PIN_2},
                        .mosi = {GPIOC_BASE, GPIO_PIN_3}
                    };
                case SPI3_BASE:
                    return {
                        .cs   = {GPIOC_BASE, GPIO_PIN_9},// 软件片选
                        .sck  = {GPIOC_BASE, GPIO_PIN_10},
                        .miso = {GPIOC_BASE, GPIO_PIN_11},
                        .mosi = {GPIOC_BASE, GPIO_PIN_12}
                    };
                default:
                    static_assert(SPI_Base != 0, "Unsupported SPI base address");
                    return {}; // 返回空配置，但编译期static_assert会阻止
            }
        }
        // 编译期常量配置
        using cs_pin = gpio::pinOld<getPinConfig().cs.port,getPinConfig().cs.pin>;
        using sck_pin = gpio::pinOld<getPinConfig().sck.port,getPinConfig().sck.pin>;
        using miso_pin = gpio::pinOld<getPinConfig().miso.port,getPinConfig().miso.pin>;
        using mosi_pin = gpio::pinOld<getPinConfig().mosi.port,getPinConfig().mosi.pin>;


    public:
        static SPI_HandleTypeDef *getHandle() { return &hspi; }
        static DMA_HandleTypeDef *getDMAHandle_rx() { return &hdma_spi_rx; }
        static DMA_HandleTypeDef *getDMAHandle_tx() { return &hdma_spi_tx; }
        static uint32_t get_SPI_DR_ADDR() { return reinterpret_cast<uint32_t>(&(hspi.Instance->DR)); }

        /**
* @brief 初始化SPI2接口
* @details 配置为全双工主模式，硬件片选引脚PB12
*          SCK=42MHz (APB1时钟84MHz / 2分频)
*/
        template<bool hard_cs = false>
        static void init() noexcept
        {
            using namespace hal::gpio;
            enable_clock();

            sck_pin::template init<mode::alternate_pp,pull::none,speed::very_high,getAlternate()>();
            miso_pin::template init<mode::alternate_pp,pull::none,speed::very_high,getAlternate()>();
            mosi_pin::template init<mode::alternate_pp,pull::none,speed::very_high,getAlternate()>();
            if constexpr  (hard_cs)
                cs_pin::template init<mode::alternate_pp,pull::none,speed::very_high,getAlternate()>();
            else
                cs_pin::template init<mode::output_pp,pull::none,speed::very_high,getAlternate()>();

            cs_pin::set();

            // SPI参数配置
            hspi.Instance = getInstance();
            hspi.Init = {
                    .Mode = SPI_MODE_MASTER,
                    .Direction = SPI_DIRECTION_2LINES,
                    .DataSize = SPI_DATASIZE_8BIT,
                    .CLKPolarity = SPI_POLARITY_LOW,
                    .CLKPhase = SPI_PHASE_1EDGE,
                    .NSS = SPI_NSS_SOFT,
                    .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2,
                    .FirstBit = SPI_FIRSTBIT_MSB,
                    .TIMode = SPI_TIMODE_DISABLE,
                    .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
                    .CRCPolynomial = 10};

            if (HAL_SPI_Init(&hspi) != HAL_OK)
            {
                Error_Handler();
            }


#ifdef ENABLE_DMA_SPI
            if constexpr (SPI_Base==SPI2_BASE)
                dma_init();
#endif
        }

        /**
         * @brief 拉低SPI2片选信号
         */
        static void cs_low() noexcept { cs_pin::clear(); }

        /**
         * @brief 拉高SPI2片选信号
         */
        static void cs_high() noexcept{ cs_pin::set(); }

        /**
         * @brief SPI2单字节阻塞传输
         * @param data 要发送的字节数据
         */
        static void send_byte(uint8_t data) noexcept
        {
            static uint8_t tx_data = 0;
            tx_data = data;
            HAL_SPI_Transmit(&hspi, &tx_data, 1, HAL_MAX_DELAY);
        }

        /**
         * @brief SPI2多字节阻塞传输
         * @param data 数据缓冲区指针
         * @param len 数据长度
         */
        static void send_bytes(uint8_t *data, uint32_t len) noexcept
        {
            HAL_SPI_Transmit(&hspi, data, len, HAL_MAX_DELAY);
        }
        /**
         * @brief SPI2单字节阻塞接收
         * @param data 接收到的字节数据
         */
        static void receive_byte(uint8_t& data) noexcept
        {
            HAL_SPI_Receive(&hspi, &data, 1, HAL_MAX_DELAY);
        }
        /**
         * @brief SPI2多字节阻塞接收
         * @param data 数据缓冲区指针
         * @param len 数据长度
         */
        static void receive_bytes(uint8_t *data, uint32_t len) noexcept
        {
            HAL_SPI_Receive(&hspi, data, len, HAL_MAX_DELAY);
        }

        /**
         * @brief SPI2单字节阻塞发送和接收
         * @param tx_data 发送数据缓冲区指针
         * @param rx_data 接收数据缓冲区指针
         */
        static void send_receive_byte(uint8_t tx_data, uint8_t& rx_data) noexcept
        {
            HAL_SPI_TransmitReceive(&hspi, &tx_data, &rx_data, 1, HAL_MAX_DELAY);
        }

        /**
         * @brief SPI2单字节阻塞发送和接收
         * @param tx_data 发送数据缓冲区指针
         * @param rx_data 接收数据缓冲区指针
         * @param len 数据长度
         */
        static void send_receive_bytes(uint8_t *tx_data, uint8_t *rx_data, uint32_t len) noexcept
        {
            HAL_SPI_TransmitReceive(&hspi, tx_data, rx_data, len, HAL_MAX_DELAY);
        }

    };
}// namespace hal::detail_spi


namespace hal
{
    using spi1 = detail_spi::spi<SPI1_BASE>;   //待分配
    using spi2 = detail_spi::spi<SPI2_BASE>;   //分配给LCD
    using spi3 = detail_spi::spi<SPI3_BASE>;   //分配给W25QXX
}